﻿/****************************************************************************
 * KinectGestureDetector
 * Main class for reading from the Kinect and propagating detected events
 * 
 * 20110526 - md - Initial version 
 * *************************************************************************/
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using OpenNI;
using NITE;
using System.Threading;
using System.Collections;

namespace ConsoleServer
{
    class KinectGestureDetector : PointControl
    {
        // NITE Gesture Controls
        private GestureGenerator gg;
        private SwipeDetector sd;
        private SessionManager sm;
        private SteadyDetector steady;      
        private WaveDetector wave;
        private CircleDetector circle;

        // Custom KinectEventServer Events
        public event EventHandler<GestureRecognizedEventArgs> ClickGesture;
        public event EventHandler<VelocityAngleEventArgs> SwipeDownGesture;
        public event EventHandler<VelocityAngleEventArgs> SwipeUpGesture;
        public event EventHandler<VelocityAngleEventArgs> SwipeLeftGesture;
        public event EventHandler<VelocityAngleEventArgs> SwipeRightGesture;
        public event EventHandler<DirectionVelocityAngleEventArgs> GeneralSwipeGesture;
        public event EventHandler<PositionEventArgs> SessionStartEvent;
        public event EventHandler<EventArgs> SessionEndEvent;
        public event EventHandler<EventArgs> WaveGesture;
        public event EventHandler<SteadyEventArgs> SteadyEvent;
        public event EventHandler<CircleEventArgs> OnCircleEvent;
        public event EventHandler<NoCircleEventArgs> NoCircleEvent;

        /**
         *  Handle "GestureRecognized Event"
         */
        void  gg_GestureRecognized(object sender, GestureRecognizedEventArgs e) {
            Console.WriteLine("Gesture: {0} at {1},{2},{3}", e.Gesture, e.IdentifiedPosition.X, e.IdentifiedPosition.Y, e.IdentifiedPosition.Z);            
            if (e.Gesture.Equals("Click"))
            {
                if (ClickGesture!=null) ClickGesture(this, e); ;
            }
        }
        /**
         *  Handle Swipe down event
         */
        void sd_SwipeDown(object sender, VelocityAngleEventArgs e)
        {
            Console.WriteLine("Swipe down detected! Velocity: {0}. Angle: {1}", e.Velocity, e.Angle);
            if (SwipeDownGesture != null) SwipeDownGesture(this, e);
        }
        void sd_SwipeUp(object sender, VelocityAngleEventArgs e)
        {
            Console.WriteLine("Swipe up detected! Velocity: {0}. Angle: {1}", e.Velocity, e.Angle);
            if (SwipeUpGesture != null) SwipeUpGesture(this, e);
        }
        void sd_SwipeRight(object sender, VelocityAngleEventArgs e)
        {
            Console.WriteLine("Swipe right detected! Velocity: {0}. Angle: {1}", e.Velocity, e.Angle);
            if (SwipeRightGesture != null) SwipeRightGesture(this, e);
        }
        void sd_SwipeLeft(object sender, VelocityAngleEventArgs e)
        {
            Console.WriteLine("Swipe left detected! Velocity: {0}. Angle: {1}", e.Velocity, e.Angle);
            if (SwipeLeftGesture != null) SwipeLeftGesture(this, e);
        }
        void sd_GeneralSwipe(object sender, DirectionVelocityAngleEventArgs e)
        {
            Console.WriteLine("Swipe detected! Direction: {0}, Velocity: {1}. Angle: {2}", e.Direction, e.Velocity, e.Angle);
            if (GeneralSwipeGesture != null) GeneralSwipeGesture(this, e);
        }
        void sm_SessionStart(object sender, PositionEventArgs e)
        {
            Console.WriteLine("Session started! Position: {0},{1},{2}", e.Position.X
                , e.Position.Y, e.Position.Z);
            if (SessionStartEvent != null) SessionStartEvent(this, e);
        }

        void sm_SessionEnd(object sender, EventArgs e)
        {
            Console.WriteLine("Session ended!");
            if (SessionEndEvent != null) SessionEndEvent(this, e);
        }

        void steady_Steady(object sender, SteadyEventArgs e)
        {
            Console.WriteLine("Steady! Deviation: {0}", e.StdDev);
            if (SteadyEvent != null) SteadyEvent(this, e);
        }

        void wave_Wave(object sender, EventArgs e)
        {
            Console.WriteLine("Wave!");
            if (WaveGesture != null) WaveGesture(this, e);
        }

        void circle_OnCircle(object sender, CircleEventArgs e)
        {
            Console.WriteLine("Circle!");
            if (OnCircleEvent != null) OnCircleEvent(this, e);
        }

        void circle_OnNoCircle(object sender, NoCircleEventArgs e)
        {
            Console.WriteLine("No circle!");
            if (NoCircleEvent != null) NoCircleEvent(this, e);
        }       
        
        /**
         * Enumerates the available OpenNI primitive gestures
         */
        public String enumerateGestures()
        {
            String[] gestures = gg.EnumerateAllGestures();
            String gesturesList = "";
            foreach (String g in gestures) {
                gesturesList += g + " ";
            }
            return gesturesList.Trim();
        }

        /**
         * Set new steady duration for swipe detector
         */

        public void setSteadyDurationForSwipe(int duration)
        {
            if (duration < 100) return;
            this.sd.SteadyDuration = duration;
        }

        /**
         * Set new steady duration for steady detector
         */

        public void setSteadyDuration(int duration)
        {
            if (duration < 100) return;
            this.steady.DetectionDuration = duration;            
        }

        /**
         * Set require steady for swipe detector
         */

        public void setUseSteadyForSwipe(bool use)
        {
            this.sd.UseSteady = use;
        }
        /**
         * Connects to the Kinect and attaches all our listeners
         */
        public void setUpAndRun() {
            Context ctx; 
            try {
                ctx = new Context(@"SamplesConfig.xml");
            }            
            catch (Exception e) {
                Console.WriteLine("ERROR: Kinect could not be attached! - " + e.Message);
                return;
            }

            // Create and attach GestureGenerator
            gg = new GestureGenerator(ctx);
            gg.GestureRecognized += gg_GestureRecognized;
            String[] gestures = gg.EnumerateAllGestures();

            // Add gesture detectors
            // "MoveHand" gesture does not seem to be raised at all "RaiseHand" is raised all the time
            foreach (String g in gestures)
            {
                Console.WriteLine("Available gesture: {0}", g);
                if (!g.Equals("RaiseHand")) // Raise hand will flood with events. Bug?
                {
                    gg.AddGesture(g);
                }
            }

            // Add SessionManager (starts on "Wave" gesture)
            sm = new SessionManager(ctx, "Wave", "RiseHand");
            sm.SessionStart += sm_SessionStart;
            sm.SessionEnd += sm_SessionEnd;
                       
            // Add SteadyDetector and link events
            steady = new SteadyDetector();
            steady.DetectionDuration = 1000; // 1s 
            steady.Steady += steady_Steady;

            // Add SwipeDetector and link events
            sd = new SwipeDetector();
            sd.UseSteady = true; // Wait for Steady before detecting events
            sd.SteadyDuration = 1000;          
            sd.GeneralSwipe += sd_GeneralSwipe;
            sd.SwipeDown += sd_SwipeDown;
            sd.SwipeUp += sd_SwipeUp;
            sd.SwipeLeft += sd_SwipeLeft;
            sd.SwipeRight += sd_SwipeRight;


            // Add WaveDetector and link events
            wave = new WaveDetector();
            wave.Wave += wave_Wave;

            // Add CircleDetector and link events
            circle = new CircleDetector();
            circle.OnCircle += circle_OnCircle;
            circle.OnNoCircle += circle_OnNoCircle;                        
            
            // Attach controls to sessionmanager
            sm.AddListener(sd);
            sm.AddListener(steady);
            sm.AddListener(wave);
            sm.AddListener(circle);
            ctx.StartGeneratingAll();
            
            while (true)
            {
                ctx.WaitAndUpdateAll();
                sm.Update(ctx);
            }

        }      
    }
}
